#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "camera_video_sink.h"
#include <QLabel>

#include "opencv2/opencv.hpp"
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include "cvhelper.h"
#include "cv_ssd.h"
#include <list>
#include <mutex>
#include <QFileDialog>
#include "Qss.h"

class  ASyncDetectAndRenderThread :public QSSASyncProcess{
public:
    typedef enum {
        STATUS_STOP = 1,
        STATUS_RUNNING = 2
    }Status;
    ASyncDetectAndRenderThread(QWidget * parent,CPlayWidget *render_ui,
                               int width,int height,
                               std::string model_path,
                               std::string model_txt_path){
        m_render = render_ui;
        m_parent = parent;
        m_status = STATUS_RUNNING;
        m_width = width;
        m_height = height;
        this->m_model_path = model_path;
        this->m_model_txt_path = model_txt_path;
    }
    bool Detecting(){
        if(m_mat.size() > 0)
            return true;
        else
            return false;
    }
    Status DetectStatus(){
        return this->m_status;
    }
    void SetCvImage(uint8_t *frame){
        cv::Mat yuv420p;
        yuv420p.create(m_height*3/2,
                       m_width, CV_8UC1);
        memcpy(yuv420p.data, frame, m_height*m_width
               *sizeof(unsigned char)*3/2);
        cv::Mat *rgbImg = new cv::Mat;
        cv::cvtColor(yuv420p, *rgbImg, cv::COLOR_YUV2BGR_I420);
        std::lock_guard<std::mutex> guard(this->m_mutex);
        m_mat.push_back(rgbImg);
    }

    void Run(void *) override{
        while(m_status == STATUS_RUNNING){
            cv::Mat *c = takeLast();
            if(nullptr != c){
                cv::Mat *result = ssd_detect(c,
                                    this->m_model_path,
                                    this->m_model_txt_path);

                qDebug()<<result->cols<<result->rows<<result->type();
                cv::Mat yuvData;

                cv::cvtColor(*result, yuvData, cv::COLOR_BGR2YUV_I420);
                this->m_render->OnCameraData(yuvData.data);

            }
            delete c;
        }
    }

private:
    cv::Mat *takeLast(){
        std::lock_guard<std::mutex> guard(this->m_mutex);
        if(m_mat.size() != 0){
            auto ret = *m_mat.begin();
            m_mat.pop_front();
            return ret;
        }
        return nullptr;
     }
    std::list<cv::Mat*> m_mat;
    std::mutex m_mutex;
    CPlayWidget *m_render;
    QWidget *m_parent;
    Status m_status;
    int m_width;
    int m_height;
    std::string m_model_path;        // 模型路径
    std::string m_model_txt_path;    // 模型txt路径
};

ASyncDetectAndRenderThread *gdetect = nullptr;

class AsyncRennder :public QSSASyncProcess{
public:
    AsyncRennder(QWidget * parent,FrameBufferList* p,CPlayWidget *render_ui){
        this->mParent = parent;
        this->mfbs = p;
        mUI = render_ui;
        this->state = true;

    }
    // 停止渲染
    void StopRender(){
        state = false;
    }
    void Run(void *) override{
        QEventLoop eventLoop;
        auto qtimer = new QTimer(this);
        qtimer->setSingleShot(false);

        QObject::connect(qtimer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
        qtimer->start(22);

        while(state){
            if(mfbs ->Size() > 0){
                uint8_t *frame = mfbs->TakeLast();
                mUI->OnCameraData(frame);
                if(gdetect != nullptr){
                    if(!gdetect->Detecting()){
                        gdetect->SetCvImage(frame);
                    }
                }
                eventLoop.exec(); // 渲染一次
                delete frame;
            }
        }
    }
private:
    FrameBufferList *mfbs;
    bool state = false;
    CPlayWidget *mUI;
};


AsyncRennder *gRender = nullptr;


MainWindow::MainWindow(QWidget *parent)
    : QssMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->openGLWidget->show();

    std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(
      webrtc::VideoCaptureFactory::CreateDeviceInfo());
    if (!info) {
        RTC_LOG(LERROR) << "CreateDeviceInfo failed";
    }
    int num_devices = info->NumberOfDevices();
    for (int i = 0; i < num_devices; ++i) {
        char name[100];
        info->GetDeviceName(i,name,100,nullptr,0,nullptr,0);
        ui->comboBox->addItem(QString::asprintf("%s",name),i);
    }
    mDetectResut = new CPlayWidget(this);
    ui->gridLayout->addWidget(mDetectResut,0,1);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::OnUpdateFrame( rtc::scoped_refptr<webrtc::I420BufferInterface>& buffer)
{
    ui->openGLWidget->OnCameraData(buffer);
}

void MainWindow::on_pushButton_clicked()
{
    if(ui->pushButton->text() == QString("采集")) {
        int id = ui->comboBox->currentData().toInt();
        webrtc::VideoCaptureCapability p;

        std::unique_ptr<webrtc::VideoCaptureModule::DeviceInfo> info(
          webrtc::VideoCaptureFactory::CreateDeviceInfo());

        char ids[128];
        info->GetDeviceName(id,nullptr,0,ids,128,nullptr,0);
        info->GetCapability(ids,id,p);
        qDebug()<<QString::asprintf("GetCapability:    %d %d %d %d",id,p.width,p.height,p.maxFPS);

        m_capturer.reset(CameraVideoSink::Create(p.width, p.height, 25, id));
        if (!m_capturer) {
            qDebug()<<"error";
        }
        ui->openGLWidget->SetDataType(CPlayWidget::TYPE_YUV420P);
        ui->openGLWidget->SetImgSize(m_capturer->Capability().width,
                                     m_capturer->Capability().height);
        ui->openGLWidget->StartRender();
    //    ui->openGLWidget->moveToThread(&gRender->Thread());
        if(gRender == nullptr){
            gRender = new AsyncRennder(this,m_capturer->VideoBuffer(),ui->openGLWidget);
            gRender->Start(this);
            connect(gRender,&QSSASyncProcess::Done,this,&MainWindow::RenderDone);
        }
        mDetectResut->SetImgSize(480,560);
        mDetectResut->StartRender();

        ui->pushButton->setText(QString::asprintf("正在采集"));
    }else{
        ui->openGLWidget->StopRender();
    }
}

void MainWindow::RenderDone()
{

}

cv::Mat *QImage2cvMat(QImage image)
{
    cv::Mat *mat;
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = new cv::Mat(image.height(),
                      image.width(),
                      CV_8UC4);
        memcpy(mat->data,image.bits(),image.bytesPerLine()*image.height());
        break;
    case QImage::Format_RGB888:
        mat = new cv::Mat(image.height(), image.width(), CV_8UC3,
                          (void*)image.bits(), image.bytesPerLine());
        break;
    case QImage::Format_Indexed8:
        mat = new cv::Mat(image.height(), image.width(), CV_8UC1,
                          (void*)image.bits(), image.bytesPerLine());
        break;
    }
    Save1BitImage(*mat,"d://tgest.png");
    return mat;
}


void MainWindow::on_pushButton_2_clicked()
{
    QPixmap pix = ui->openGLWidget->grab();
    origin_picture->setPixmap(pix);
}

// 识别
void MainWindow::on_pushButton_3_clicked()
{
    if(nullptr != origin_picture->pixmap()){
        QImage image = origin_picture->pixmap()->toImage();
        image = image.scaled(1280,720);
        cv::Mat pic =  *QImage2cvMat(image);
        if((ui->lineEdit->text() == "") || (ui->lineEdit_2->text() == "")){

        }
        cv::Mat *result = ssd_detect(&pic,
                                      ui->lineEdit->text().toStdString(),
                                      ui->lineEdit_2->text().toStdString()
        );
        this->processed_picture->setPixmap(QPixmap::fromImage(image));
    }
}


void MainWindow::on_pushButton_4_clicked()
{
    if(gdetect == nullptr){
        if((ui->lineEdit->text().toStdString() == "")
                ||
                (ui->lineEdit_2->text().toStdString() == "")){
            QssMessageBox::warn("please input model file",nullptr,"please input model file",QMessageBox::Ok);
            return;
        }
        gdetect = new ASyncDetectAndRenderThread(this,mDetectResut,ui->openGLWidget->RenderWidth(),
                                                 ui->openGLWidget->RenderHeight(),
                                                 ui->lineEdit->text().toStdString(),
                                                 ui->lineEdit_2->text().toStdString());
        gdetect->Start(this);
        connect(gdetect,&QSSASyncProcess::Done,this,&MainWindow::RenderDone);
    }
}


// 导入文件
void MainWindow::on_pushButton_5_clicked()
{
    QString label1 = ui->lineEdit_2->text();
    QString openFile = QFileDialog::getOpenFileName(this, "0.0", "",
                            "*.caffemodel *.prototxt",nullptr);
    if (openFile.contains(".caffemodel")){
        ui->lineEdit->setText(openFile);
    }
    if (openFile.contains(".prototxt")){
        ui->lineEdit_2->setText(openFile);
    }
}

